#ifndef QAes_H
#define QAes_H

#include <QObject>
#include <QByteArray>

class QAesMgr : public QObject
{
    Q_OBJECT
public:
    enum Aes
    {
        AES_128,    //16位密钥
        AES_192,   //24位密钥
        AES_256,   //32位密钥
    };

    enum Mode
    {
        ECB,   //不带偏移IV
        CBC,
        CFB,
        OFB,
    };

    enum Padding
    {
      ZERO,
      PKCS7,
      ISO,
    };

    static QByteArray Crypt(QAesMgr::Aes Level, QAesMgr::Mode Mode, const QByteArray &RawText, const QByteArray &Key, const QByteArray &IV = Q_NULLPTR, QAesMgr::Padding Padding = QAesMgr::ISO);

    static QByteArray Decrypt(QAesMgr::Aes Level, QAesMgr::Mode Mode, const QByteArray &RawText, const QByteArray &Key, const QByteArray &IV = Q_NULLPTR, QAesMgr::Padding Padding = QAesMgr::ISO);

    static QByteArray ExpandKey(QAesMgr::Aes Level, QAesMgr::Mode Mode, const QByteArray &Key);
    static QByteArray RemovePadding(const QByteArray &RawText, QAesMgr::Padding Padding);

    QAesMgr(QAesMgr::Aes Level, QAesMgr::Mode Mode, QAesMgr::Padding Padding = QAesMgr::ISO);

    QByteArray Encode(const QByteArray &RawText, const QByteArray &Key, const QByteArray &IV = Q_NULLPTR);
    QByteArray Decode(const QByteArray &RawText, const QByteArray &Key, const QByteArray &IV = Q_NULLPTR);

    QByteArray removePadding(const QByteArray &RawText);
    QByteArray expandKey(const QByteArray &Key);

signals:

public slots:

private:
    int MNb;
    int MBlocklen;
    int MLevel;
    int MMode;
    int MNk;
    int MKeyLen;
    int MNr;
    int MExpandedKey;
    int MPadding;
    QByteArray* MState;

    struct AES256
    {
        int Nk = 8;
        int KeyLen = 32;
        int Nr = 14;
        int ExpandedKey = 240;
    };

    struct AES192
    {
        int Nk = 6;
        int KeyLen = 24;
        int Nr = 12;
        int ExpandedKey = 209;
    };

    struct AES128
    {
        int Nk = 4;
        int KeyLen = 16;
        int Nr = 10;
        int ExpandedKey = 176;
    };

    quint8 GetSBoxValue(quint8 num){ return SBox[num]; }
    quint8 GetSBoxInvert(quint8 num){ return RSBox[num]; }

    void AddRoundKey(const quint8 Round, const QByteArray ExpKey);
    void SubBytes();
    void ShiftRows();
    void MixColumns();
    void InvMixColumns();
    void InvSubBytes();
    void InvShiftRows();
    QByteArray GetPadding(int CurrSize, int Alignment);
    QByteArray Cipher(const QByteArray &ExpKey, const QByteArray &PlainText);
    QByteArray InvCipher(const QByteArray &ExpKey, const QByteArray &PlainText);
    QByteArray ByteXor(const QByteArray &In, const QByteArray &IV);

    static const quint8 SBox[256];
    static const quint8 RSBox[256];
    static const quint8 Rcon[256];
};

#endif // QAes_H
